/**
 * 
 */
package org.dragonfire.ti;

import org.dragonfire.util.DMath;

/**
 * @author Darth Android
 * @date Dec 26, 2007
 */
public class TIReal extends VarEntry implements TIConstants {

	/**
	 * @param calcType
	 * @param varType
	 * @param varName
	 * @param data
	 */
	public TIReal(int calcType, Byte[] varName, Byte[] data) {
		super(calcType, TIFile.getCalcVarType(calcType, TIFile.TYPE_REAL_NUMBER), varName, data);
		setData(0);
	}

	public static double convertToJavaDouble(Byte[] tiDouble) {
		double output = 0;
		byte flags = tiDouble[0];
		int exp = 0;
		byte[] mantissa = new byte[0];
		boolean negative = false;
		switch (tiDouble.length) {
			case 9:
				exp = (byte) (tiDouble[1] - 0x80);
				mantissa = new byte[7];
				System.arraycopy(tiDouble, 2, mantissa, 0, 7);
				negative = (flags & 0x08) > 0;
				break;
			case 10:
				exp = (tiDouble[2] << 8) + tiDouble[1] - 0xFC00;
				mantissa = new byte[7];
				System.arraycopy(tiDouble, 3, mantissa, 0, 7);
				negative = (flags & 0x08) > 0;
				break;
		}
		// reconstruct the mantissa
		/*
		 * Note that this creates a mantissa that is a power of ten less than
		 * the original. This is corrected when the exponent is added to the
		 * number
		 */
		for (int i = mantissa.length - 1; i >= 0; i--) {
			int add = Integer.parseInt(DMath.changeBase((mantissa[i] & 0xFF) + "", 10, 16));
			output += add;
			output /= 100;
		}
		// fix the exponent on the mantissa
		// the +1 adjusts for the error in reconstructing the mantissa
		output = output * Math.pow(10, exp + 1);
		// set the negativity
		return negative ? -output : output;
	}

	/**
	 * Converts a java <code>double</code> type into a 9- or 10-byte
	 * TI-formatted double. A Z80 double is 9 bytes in length and only uses one
	 * byte for the exponent while a M68000 double uses 10 bytes and a 2-byte
	 * exponent.
	 * 
	 * @param javaDouble
	 *            The double to convert
	 * @param Z80Format
	 *            The output format of the TI-double. <br />
	 *            <code>true</code> - Generates a TI-formatted double for the
	 *            Z80 calculators<br />
	 *            <code>false</code> - Generates a TI-formatted double for the
	 *            M68000 calculators
	 * @return A <code>Byte</code> array containing the raw representation of
	 *         <code>javaDouble</code> as a TI-formatted double.
	 */
	public static Byte[] convertToTIDouble(double javaDouble, boolean Z80Format) {
		Byte[] tiDouble = new Byte[9];
		tiDouble[0] = javaDouble < 0 ? (byte) 0x80 : 0x00;
		int exp = (int) Math.pow(javaDouble, 0.1) - 1;
		tiDouble[1] = (byte) (exp + 0x80);
		javaDouble *= Math.pow(10, exp - 1);
		System.out.println(javaDouble + "\n" + exp);
		for (int i = 0; i < 7; i++) {
			javaDouble *= 100;
			tiDouble[i + 2] = (byte) Integer.parseInt(DMath.changeBase((int) javaDouble + "", 16, 10));
			javaDouble = javaDouble - (int) javaDouble;
		}
		for (byte b : tiDouble)
			System.out.printf("%02x", b);
		System.out.println();
		return tiDouble;
	}

	/**
	 * {@inheritDoc}
	 */
	public void setData(Byte[] data) {
		super.setData(data);
	}

	/**
	 * Retrieves the contents of this TIReal and formats it as a java double.
	 * 
	 * @return A java double representation of this TI double
	 */
	public double getDoubleData() {
		return convertToJavaDouble(getData());
	}

	/**
	 * Sets this <code>TIReal</code>'s value to the specified value.
	 * 
	 * @param value
	 *            The value to change to.
	 */
	public void setData(double value) {
		/*
		 * since we know convertToTIDouble() generates valid doubles, we can
		 * bypass setData()'s format checks
		 */
		super.setData(convertToTIDouble(value, !(getCalc() == CALC_89 || getCalc() == CALC_92)));
	}

	public static void main(String[] s) {
		System.out.print("\n" + convertToJavaDouble(convertToTIDouble(Math.PI, true)));
	}
}
